﻿using FreeSql;
using Singer.Core;
using Singer.Shared.Domain;
using Singer.Shared.Modularity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Singer.Middleware.FreeSql
{
    public abstract class BaseRepository<TEntity> : global::FreeSql.BaseRepository<TEntity>, IBaseRepository<TEntity>
        where TEntity : class, IEntity
    {
        bool IsSoftDelete => typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity));
        IUserContextAccessor _userContextAccessor;
        IUserContext? _userContext => _userContextAccessor.UserContext;
        public BaseRepository(IFreeSql fsql, IUserContextAccessor userContextAccessor, Expression<Func<TEntity, bool>>? filter = null, Func<string, string>? asTable = null) : base(fsql, filter, asTable)
        {
            _userContextAccessor = userContextAccessor;
        }

        private SolftDeleteDto CurrentSolftDeleteDto => new SolftDeleteDto(_userContext?.UserId);

        public override int Delete(Expression<Func<TEntity, bool>> predicate)
        {
            predicate.CheckNull(nameof(predicate), msg: "【FreeSql】方法'Delete' 参数'predicate(条件表达式)'不可为空.");
            if (!IsSoftDelete)
                return base.Delete(predicate);
            return Orm.Update<TEntity>().Where(predicate).SetDto(CurrentSolftDeleteDto).ExecuteAffrows();
        }

        public override int Delete(IEnumerable<TEntity> entitys)
        {
            if (!IsSoftDelete)
                return base.Delete(entitys);
            return Orm.Update<TEntity>(entitys).SetDto(CurrentSolftDeleteDto).ExecuteAffrows();
        }

        public override int Delete(TEntity entity)
        {
            if (!IsSoftDelete)
                return base.Delete(entity);
            return Orm.Update<TEntity>(entity).SetDto(CurrentSolftDeleteDto).ExecuteAffrows();
        }

        public override Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
        {
            predicate.CheckNull(nameof(predicate), msg: "【FreeSql】方法'Delete' 参数'predicate(条件表达式)'不可为空.");
            if (!IsSoftDelete)
                return base.DeleteAsync(predicate, cancellationToken);
            return Orm.Update<TEntity>().Where(predicate).SetDto(CurrentSolftDeleteDto).ExecuteAffrowsAsync(cancellationToken);
        }

        public override Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
        {
            if (!IsSoftDelete)
                return base.DeleteAsync(entitys, cancellationToken);
            return Orm.Update<TEntity>(entitys).SetDto(CurrentSolftDeleteDto).ExecuteAffrowsAsync();
        }

        public override Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
        {
            if (!IsSoftDelete)
                return base.DeleteAsync(entity, cancellationToken);
            return Orm.Update<TEntity>(entity).SetDto(CurrentSolftDeleteDto).ExecuteAffrowsAsync(cancellationToken);
        }

        public bool Any(Expression<Func<TEntity, bool>>? exp) =>
            Orm.Select<TEntity>().Any(exp);

        public Task<bool> AnyAsync(Expression<Func<TEntity, bool>>? exp) =>
            Orm.Select<TEntity>().AnyAsync(exp);

        public TEntity? Get(Expression<Func<TEntity, bool>>? exp) =>
            Orm.Select<TEntity>().Where(exp).First();

        public Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>>? exp) =>
            Orm.Select<TEntity>().Where(exp).FirstAsync();

        public TDto? Get<TDto>(Expression<Func<TEntity, bool>>? exp) where TDto : class =>
            Orm.Select<TEntity>().Where(exp).First<TDto>();

        public Task<TDto?> GetAsync<TDto>(Expression<Func<TEntity, bool>>? exp) where TDto : class =>
            Orm.Select<TEntity>().Where(exp).FirstAsync<TDto>();

        private class SolftDeleteDto
        {
            public string? DeleterId { get; set; }
            public bool IsDeleted { get; set; } = true;
            public DateTime? DeleteTime { get; set; } = Cores.BeiJingNow;

            public SolftDeleteDto(string? deleterId)
            {
                DeleterId = deleterId;
            }
        }
    }
}
